#include "html.h"
#include <fstream>
#include <boost/progress.hpp>
#include <boost/assign.hpp>
#include <deque>

#include "tgf.h"

using namespace html;
using namespace dom;
using namespace TrivialGraphFormat;
using namespace boost::assign;

#ifdef _DEBUG
#define DEBUG_NUM(x) 1
#else
#define DEBUG_NUM(x) (x)
#endif
#define FILENAME "ToBeDefined.htm"
#define TEMPLATENAME "cookbook.cos"

template<bool p_>
void test_selector( root_node& root, const char* _selector, const int num )
{
  selector sel = _selector;
  cout << "finding " << root(sel).size() << " nodes by " << (p_?"preparsed":"") << " selector \"" << _selector << "\" " << num << " times in ";

  progress_timer t;
  if( p_ )
    for( int i=0; i<num; i++)
      root(sel);
  else
    for( int i=0; i<num; i++)
      root(_selector);
}

static int drop_tree(node& _n, Sheet& _sheet)
{ 
  int n=_sheet.add(_n.tag());
  if( _n.id() != symbol::nil() )
    _sheet.add(Association(n,_sheet.add(_n.id()),"id"));
  for( class_set::iterator it=_n.classes().begin(); it!=_n.classes().end(); it++ )
    _sheet.add(Association(n,_sheet.add(*it),"class"));
  for( attrib_map::iterator it=_n.attribs().begin(); it!=_n.attribs().end(); it++ )
    _sheet.add(Association(n,_sheet.add(it->first),""));
  for( nodes::iterator it=_n.children().begin(); it!=_n.children().end(); it++ )
    _sheet.add(Association(n,drop_tree(*it,_sheet)));
  return n;
}

struct data {
  typedef vector<string> row;
  typedef row columns_type;
  typedef vector< row >::const_iterator const_iterator;

  const row& columns() { return columns_; }
  
  const_iterator begin() { return data_.begin(); }
  const_iterator end() { return data_.end(); }

  // private:
  vector< row > data_;
  row columns_;
};

////////////////////////////////////////////////////////////////////////////

int main () {
  {
    const int num=DEBUG_NUM(10000);
    cout << "creating DOM (+destroy) " << num << " times in ";

    progress_timer t;
    for( int i=0; i<num; i++)
    {
      // will hold a reference to a span tag in the following Div definition
      node hugo;
      // Div definition
      node userlist = 
        // making a div with id and class attributes set
        Div[ Id="userlist", Class="table",
          // add a child div with class attribute set
          Div[ Class="user", 
            // add a span that contains the text "hugo" and store a reference to this span in hugo
            hugo=Span[ "hugo" ], 
            // add some more text and nodes
            " ", Span[ "meyer" ], " ", 
            // add a typically anchor tag
            A[ Href="http://somewhere.com", 
              Img [ Class="portrait", Src="../images/user.png", Alt="portait" ]
            ]
          ],
          // just another child
          Div[ Class="user last", 
            Span[ "walter" ], " ", Span[ "meyer" ]
          ]
        ];
      // overwrite the hugo tag!
      hugo = "benno";
      // check success
      if( userlist("span")[0] != "benno" )
        exit(-1);

      // hold a reference to the first div in the following nodes
      node m;
      // create a nodes collection
      nodes n=(m=Div["text"], Span["Hallo"]);
    }
  }

  {
    // example dom
    root_node root;

    ifstream  ifs(TEMPLATENAME);
    assert( ifs.is_open() );
    html::parser::read(ifs, root);

    map<string,string> row; {
      row["recipe"] = "Hot Chips";
      row["link"] = "http://www.wtf.com";
      row["delete_link"] = "http://www.wtf.com?delete";
      row["category"] = "Snacks";
      row["date"] = "2005-01-23";
    }

    root("table tbody tr")[0] = row;
    root("table tbody tr")[0] *= 4;
    nodes ns=root("table tbody tr");
    ns[0]("td")[0] = "recipe0";
    ns[1]("td")[0] = "recipe1";
    ns[2]("td")[0] = "recipe2";
    ns[3]("td")[0] = "recipe3";
  }

  {
    data mydata;
    mydata.columns_ = list_of("id")("name")("sex");

    mydata.data_.push_back(list_of("1")("cindy")("f"));
    mydata.data_.push_back(list_of("2")("mandy")("f"));
    mydata.data_.push_back(list_of("3")("ken")("m"));
    mydata.data_.push_back(list_of("4")("barby")("f"));  
  }

  {
    // example dom
    root_node root;

    // test reading a file
    {
      const int num=DEBUG_NUM(10);
      cout << "reading file (+destroy) " << num << " times in ";

      ifstream  ifs(FILENAME);
      assert( ifs.is_open() );
      html::parser::read(ifs, root);

      progress_timer t;
      for( int i=0; i<num; i++)
      {
        ifstream  ifs(FILENAME);
        root_node r;
        html::parser::read(ifs, r);
      }
    }

    // export TGF file
    {
      Sheet sheet;
      ofstream os_tgf("out.tgf");
      drop_tree(root,sheet);
      sheet.dump(os_tgf);
    }

    // test nodes counting
    {
      const int num=DEBUG_NUM(100000);
      cout << "counting " << num << " times " << root.count() << " nodes in ";

      progress_timer t;
      for( int i=0; i<num; i++)
        root.count();
    }

    // test cloning
    {
      const int num=DEBUG_NUM(1000);
      cout << "cloning root (+destroy) " << num << " times in ";

      progress_timer t;
      for( int i=0; i<num; i++)
        root_node root1 = root.clone();
    }

    // test id search
    {
      const int num=DEBUG_NUM(10000);
      cout << "finding node by id='footer-wrapper' " << num << " times in ";

      progress_timer t;
      for( int i=0; i<num; i++)
        root["footer-wrapper"];
    }
    
    // test several selectors
    test_selector<false>( root, "span", DEBUG_NUM(10000) );
    test_selector<false>( root, "div.clear", DEBUG_NUM(10000) );
    test_selector<true>( root, "div.clear", DEBUG_NUM(10000) );
    test_selector<false>( root, ".clear", DEBUG_NUM(10000) );
    test_selector<false>( root, "a span", DEBUG_NUM(10000) );

    // test writing a file
    {
      const int num=DEBUG_NUM(100);
      cout << "writing file " << num << " times in ";

      ofstream  ofs(FILENAME ".out.html");
      assert( ofs.is_open() );
      root.dump(ofs);

      progress_timer t;
      for( int i=0; i<num; i++)
      {
        ofstream  ofs(FILENAME ".out.html");
        root.dump(ofs);
      }
    }
  }
  //////////////////////////////////////////

  return 0;
}

